package com.gzucm.Scholarship.service;

import com.gzucm.Scholarship.common.ConstValues;
import com.gzucm.Scholarship.common.base.BaseEntity;
import com.gzucm.Scholarship.common.base.BaseServiceImpl;
import com.gzucm.Scholarship.common.enums.UserType;
import com.gzucm.Scholarship.config.yml.GzucmSecurityConfig;
import com.gzucm.Scholarship.dao.UserDao;
import com.gzucm.Scholarship.dto.PasswordDTO;
import com.gzucm.Scholarship.dto.ResultDTO;
import com.gzucm.Scholarship.entity.Student;
import com.gzucm.Scholarship.entity.Teacher;
import com.gzucm.Scholarship.entity.UserInfo;
import com.gzucm.Scholarship.exception.ImportException;
import com.gzucm.Scholarship.util.CookieUtil;
import com.gzucm.Scholarship.util.MD5Util;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
public class UserService extends BaseServiceImpl<UserInfo> {

    private UserDao userDao;

    private CurrentService currentService;

    private RedisService redisService;

    private StudentService studentService;

    private TeacherService teacherService;

    private GzucmSecurityConfig security;

    public UserService(UserDao dao, CurrentService currentService, RedisService redisService, StudentService studentService, TeacherService teacherService, GzucmSecurityConfig security) {
        super(dao);
        this.userDao = dao;
        this.currentService = currentService;
        this.redisService = redisService;
        this.studentService = studentService;
        this.teacherService = teacherService;
        this.security = security;
    }

    public ResultDTO login(UserInfo info) {
        UserInfo db = userDao.findByLoginName(info.getLoginName());
        if (Objects.isNull(db)) {
            return ResultDTO.failure(HttpStatus.NOT_FOUND, "不存在的登录名");
        }
        if (db.getDeleteflag()) {
            return ResultDTO.failure(HttpStatus.FORBIDDEN, "账号已失效");
        }
        if (Objects.equals(db.getPassword(), MD5Util.MD5(info.getPassword()))) {
            String uuid = UUID.randomUUID().toString().replace("-", "");//使用uuid作为令牌
            String key = ConstValues.REDIS_USER_LOGIN + uuid;//生产redis的key
            redisService.set(key, db, security.getExpTime());//插入缓存
            HttpServletResponse response = currentService.getHttpServletResponse();
            CookieUtil.setCookie(response, security.getTokenName(), uuid, security.getCookie().getMaxAge(), security.getCookie().getPath(), security.getCookie().getHttpOnly());
            return ResultDTO.success("登录成功");
        } else {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "密码错误");
        }
    }

    public ResultDTO logout() {
        String uuid = currentService.getToken();
        String key = ConstValues.REDIS_USER_LOGIN + uuid;//生产redis的key
        redisService.del(key);
        HttpServletResponse response = currentService.getHttpServletResponse();
        CookieUtil.setCookie(response, security.getTokenName(), "", CookieUtil.DELETE, security.getCookie().getPath(), security.getCookie().getHttpOnly());
        return ResultDTO.success("退出成功");
    }

    public ResultDTO findByType(UserType type) {
        return ResultDTO.success(userDao.findByType(type.getCode()));
    }

    @Transactional
    public ResultDTO setCadres(List<String> idList) {
        List<UserInfo> userInfos = this.findByIds(idList);
        List<UserInfo> setList = new ArrayList<>();
        userInfos.stream()
                .filter(u -> UserType.STUDENT.equals(UserType.get(u.getType())))
                .forEach(u -> {
                    u.setType(UserType.CLASS_CADRES.getCode());
                    setList.add(u);
                });
        return ResultDTO.success("设置成功", save(setList));
    }

    @Transactional
    public ResultDTO cancelCadres(List<String> idList) {
        List<UserInfo> userInfos = this.findByIds(idList);
        List<UserInfo> setList = new ArrayList<>();
        userInfos.stream()
                .filter(u -> UserType.CLASS_CADRES.equals(UserType.get(u.getType())))
                .forEach(u -> {
                    u.setType(UserType.STUDENT.getCode());
                    setList.add(u);
                });
        return ResultDTO.success("取消成功", save(setList));
    }

    public ResultDTO getUserInfo(String id) {
        String account;
        UserInfo me = currentService.getCurrentUser();
        UserType type = UserType.get(me.getType());
        if (Objects.nonNull(id) && !UserType.STUDENT.equals(type) && !UserType.CLASS_CADRES.equals(type)) {
            me = findById(id);
            if (Objects.isNull(me)) {
                return ResultDTO.failure(HttpStatus.BAD_REQUEST, "错误的用户id");
            }
            type = UserType.get(me.getType());
        }
        account = me.getAccount();
        Object info = null;
        switch (type) {
            case STUDENT:
            case CLASS_CADRES:
                info = studentService.findById(account);
                break;
            case TEACHER:
                info = teacherService.findById(account);
                break;
        }
        Map<String, Object> map = new LinkedHashMap<>();
        UserInfo user = new UserInfo();
        BeanUtils.copyProperties(me, user);
        user.setPassword(null);
        map.put("user", user);
        map.put("info", info);
        return ResultDTO.success(map);
    }

    public ResultDTO forgetPassword(PasswordDTO dto) {
        dto.setType(UserType.get(dto.getTypeCode()));
        Object obj = null;
        switch (dto.getType()) {
            case STUDENT:
            case CLASS_CADRES:
                obj = studentService.findByNumber(dto.getNumber());
                break;
            case TEACHER:
                obj = teacherService.findByNumber(dto.getNumber());
                break;
        }
        if (Objects.isNull(obj)) {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "不正确的学号/工号");
        }
        boolean isOk = false;
        switch (dto.getType()) {
            case STUDENT:
            case CLASS_CADRES:
                Student stu = (Student) obj;
                isOk = Objects.equals(stu.getIdCard(), dto.getIdCard());
                break;
            case TEACHER:
                Teacher tea = (Teacher) obj;
                isOk = Objects.equals(tea.getIdCard(), dto.getIdCard());
                break;
        }
        if (isOk) {
            UserInfo info = userDao.findByAccountIn(Collections.singletonList(((BaseEntity) obj).getId())).get(0);
            info.setPassword(MD5Util.MD5(dto.getNewPassword()));
            save(info);
            return ResultDTO.success("密码重置成功");
        } else {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "身份凭证不匹配，请联系管理员");
        }

    }

    public ResultDTO resetPassword(PasswordDTO dto) {
        UserInfo info = currentService.getCurrentUser();
        if (!Objects.equals(info.getPassword(), MD5Util.MD5(dto.getOldPassword()))) {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "旧密码不正确");
        } else {
            String key = ConstValues.REDIS_USER_LOGIN + currentService.getToken();
            UserInfo db = findById(info.getId());
            db.setPassword(MD5Util.MD5(dto.getNewPassword()));
            save(db);
            redisService.set(key, db, security.getExpTime());
            return ResultDTO.success("密码修改成功");
        }
    }

    @Transactional
    public ResultDTO importUser(Map<Integer, Map<Integer, Object>> map) {
        if (map.isEmpty()) {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "空文件");
        }
        Map<Integer, Object> header = map.get(0);
        map.remove(0);
        if (!header.containsValue("登录名")) {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "缺少标题：登录名");
        } else if (!header.containsValue("姓名")) {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "缺少标题：姓名");
        } else if (!header.containsValue("类型")) {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "缺少标题：类型");
        } else if (!header.containsValue("学号/工号")) {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "缺少标题：学号/工号");
        } else if (!header.containsValue("身份证号")) {
            return ResultDTO.failure(HttpStatus.BAD_REQUEST, "缺少标题：身份证号");
        }
        Map<String, Integer> indexMap = new LinkedHashMap<>();
        header.forEach((key, value) -> {
            indexMap.put((String) value, key);
        });

        AtomicReference<String> uuid = new AtomicReference<>();
        AtomicReference<UserInfo> info = new AtomicReference<>();
        AtomicReference<Student> student = new AtomicReference<>();
        AtomicReference<Teacher> teacher = new AtomicReference<>();
        List<Student> students = new ArrayList<>();
        List<Teacher> teachers = new ArrayList<>();
        List<UserInfo> userInfos = new ArrayList<>();
        String password = MD5Util.MD5("123456");
        map.forEach((key, value) -> {
            info.set(new UserInfo());
            info.get().setLoginName((String) value.get(indexMap.get("登录名")));
            if (Objects.nonNull(userDao.findByLoginName(info.get().getLoginName()))) {
                throw new ImportException("登录名重复:" + info.get().getLoginName());
            }
            info.get().setType(((Double) value.get(indexMap.get("类型"))).intValue());
            info.get().setPassword(password);
            uuid.set(UUID.randomUUID().toString());
            info.get().setAccount(uuid.get());
            info.get().setDeleteflag(false);
            Object temp = value.get(indexMap.get("身份证号"));
            if (!(temp instanceof String)) {
                throw new ImportException("序号:" + value.get(indexMap.get("序号")) + "的身份证号必须为文本格式");
            }
            switch (UserType.get(info.get().getType())) {
                case STUDENT:
                case CLASS_CADRES:
                    student.set(new Student());
                    student.get().setId(uuid.get());
                    student.get().setName((String) value.get(indexMap.get("姓名")));
                    student.get().setNumber(((Double) value.get(indexMap.get("学号/工号"))).intValue() + "");
                    student.get().setIdCard((String) temp);
                    student.get().setDeleteflag(false);
                    students.add(student.get());
                    break;
                case TEACHER:
                    teacher.set(new Teacher());
                    teacher.get().setId(uuid.get());
                    teacher.get().setName((String) value.get(indexMap.get("姓名")));
                    teacher.get().setNumber(((Double) value.get(indexMap.get("学号/工号"))).intValue() + "");
                    teacher.get().setIdCard((String) temp);
                    teacher.get().setDeleteflag(false);
                    teachers.add(teacher.get());
                    break;
            }
            userInfos.add(info.get());
        });
        if (!teachers.isEmpty()) {
            teacherService.save(teachers);
        }
        if (!students.isEmpty()) {
            studentService.save(students);
        }
        if (!userInfos.isEmpty()) {
            return ResultDTO.success("导入成功", this.save(userInfos));
        }
        return ResultDTO.success(HttpStatus.NO_CONTENT, "不存在要导入的内容");
    }

    public List<UserInfo> findByYear(String year) {
        List<Student> students = studentService.findByNumberStartsWith(year);
        List<String> accountList = students.stream().map(BaseEntity::getId).collect(Collectors.toList());
        return userDao.findByAccountIn(accountList);
    }

    @Transactional
    public int logicalDeleteByYear(String year) {
        List<Student> students = studentService.findByNumberStartsWith(year);
        List<String> accountList = students.stream().filter(s -> Objects.equals(s.getDeleteflag(), Boolean.FALSE)).map(BaseEntity::getId).collect(Collectors.toList());
        List<UserInfo> userInfos = userDao.findByAccountIn(accountList);
        studentService.logicalDelete(students);
        super.logicalDelete(userInfos);
        return userInfos.size();
    }

    @Override
    @Transactional
    public void logicalDelete(UserInfo entity) {
        super.logicalDelete(entity);
        switch (UserType.get(entity.getType())) {
            case TEACHER:
                teacherService.logicalDelete(entity.getAccount());
                break;
            case STUDENT:
            case CLASS_CADRES:
                studentService.logicalDelete(entity.getAccount());
                break;
        }
    }

    @Override
    @Transactional
    public void logicalDelete(Iterable<UserInfo> iterable) {
        Iterator<UserInfo> iterator = iterable.iterator();
        while (iterator.hasNext()) {
            logicalDelete(iterator.next());
        }
    }
}
